package com.tiny.tinyweb.core.dao.access;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.tiny.tinyweb.core.exception.DaoException;

/**
 * 实现动态构造SQL语句的<code>WHERE</code>子句的辅助类。
 * @author wxiaobin
 */
public final class WhereHelper {
    /**
     * 提供<code>WHERE</code>子句中条件类型的类。使用<code>int</code>型表示。
     */
    public static class Type {
        /**
         * 条件类型，默认值为“=”。
         */
        @SuppressWarnings("unused")
        private int type = 1;

        /**
         * 用来设置条件类型的构造器。
         *
         * @param type 条件类型
         */
        private Type(int type) {
            this.type = type;
        }

        /**
         * '='
         */
        public static final Type EQUAL = new Type(1);

        /**
         * '>'
         */
        public static final Type GREATER = new Type(2);

        /**
         * '<'
         */
        public static final Type LESS = new Type(3);

        /**
         * '>='
         */
        public static final Type GREATER_EQUAL = new Type(4);

        /**
         * '<='
         */
        public static final Type LESS_EQUAL = new Type(5);

        /**
         * '<>'
         */
        public static final Type NOT_EQUAL = new Type(6);

        /**
         * 'LIKE'
         */
        public static final Type LIKE = new Type(7);

        /**
         * 'BETWEEN'
         */
        public static final Type BETWEEN = new Type(8);

        /**
         * 'IN'
         */
        public static final Type IN = new Type(9);
        
        /**
         * '>= to_date'
         */
        public static final Type GREATER_EQUAL_TIME = new Type(10);
        
        /**
         * '<= to_date'
         */
        public static final Type LESS_EQUAL_TIME = new Type(11);
    }

    /**
     * 作为条件的字段。
     */
    private String column;

    /**
     * 条件类型。
     */
    private Type type;

    /**
     * 用户指定的条件值列表：
     * <ul>
     * <li>如果条件不是<code>BETWEEN</code>和<code>IN</code>，只有列表中的首项目会被用作条件。</li>
     * <li>如果条件是<code>LIKE</code>，只有列表中的首项目会被用作条件，并且需要显示设置“%”。</li>
     * <li>如果条件是<code>BETWEEN</code>，条件会被这样构造：BETWEEN values[0] AND values[1]。</li>
     * <li>如果条件是<code>IN</code>, 条件会被这样构造：IN (values)</li>
     * </ul>
     */
    private List<Object> value;

    /**
     * 获得作为条件的字段的值，如果该值没被设置，则抛出值无效异常。
     * @return 作为条件的字段的值
     */
    public String getColumn() {
        if (this.column == null || this.column.isEmpty()) {
            throw new DaoException("The column must not be null or empty.");
        }
        return this.column;
    }

    /**
     * 设置作为条件的字段的值。
     * @param column 作为条件的字段的新值
     */
    public void setColumn(String column) {
        this.column = column;
    }

    /**
     * 获得条件类型的值。
     * @return 条件类型的值
     */
    public Type getType() {
        return this.type;
    }

    /**
     * 设置条件类型的值。
     * @param type 条件类型的新值
     */
    public void setType(Type type) {
        this.type = type;
    }

    /**
     * 获得用户指定的条件值列表的值，如果该列表没有被指定，则抛出值无效异常。
     *
     * @return 用户指定的条件值列表的值
     */
    public List<Object> getValue() {
        if (this.value == null || this.value.isEmpty()) {
            throw new DaoException("The value must not be null or empty.");
        }
        return this.value;
    }

    /**
     * 设置用户指定的条件值列表的值。
     * @param value 用户指定的条件值列表的新值
     */
    public void setValue(List<Object> value) {
        this.value = value;
    }

    /**
     * 默认的无参构造器，初始化一个默认的条件“1 = 1”。
     */
    public WhereHelper() {
        this.column = "1";
        this.type = Type.EQUAL;
        this.value = new ArrayList<Object>();
        this.value.add("1");
    }

    /**
     * 指定作为条件的字段、条件类型和用户指定的条件值列表的构造器。
     *
     * @param column 作为条件的字段
     * @param type 条件类型
     * @param value 用户指定的条件值列表
     */
    public WhereHelper(String column, Type type, List<Object> value) {
        this.column = column;
        this.type = type;
        this.value = value;
    }

    public WhereHelper(String column, Type type, Object value) {
        this.column = column;
        this.type = type;
        this.value = Arrays.asList(value);
    }

    /**
     * 获得用于<code>WHERE</code>子句的条件，如果条件类型不符合SQL规范，则抛出值无效异常。
     *
     * @return 用于<code>WHERE</code>子句的条件
     */
    public String getSqlType() {
        if (this.type == Type.EQUAL) {
            return " = :" + this.column;
        } else if (this.type == Type.GREATER) {
            return " > :" + this.column + "_GT";
        } else if (this.type == Type.LESS) {
            return " < :" + this.column + "_LT";
        } else if (this.type == Type.GREATER_EQUAL) {
            return " >= :" + this.column + "_GE";
        } else if (this.type == Type.LESS_EQUAL) {
            return " <= :" + this.column + "_LE";
        } else if (this.type == Type.NOT_EQUAL) {
            return " <> :" + this.column;
        } else if (this.type == Type.LIKE) {
            return " LIKE :" + this.column;
        } else if (this.type == Type.BETWEEN) {
            return " BETWEEN :" + this.column + "_B" + " AND :" + this.column + "_E";
        } else if (this.type == Type.IN) {
            return " IN (:" + this.column + ")";
        } else if (this.type == Type.GREATER_EQUAL_TIME) {
            return " >= to_date(:" + this.column + "_GET,'YYYY/MM/DD HH24:MI')";
        } else if (this.type == Type.LESS_EQUAL_TIME) {
            return " <= to_date(:" + this.column + "_LET,'YYYY/MM/DD HH24:MI')";
        } else {
            throw new DaoException(String.format(
                    "Condition type '%1$s' is invalid.", this.type));
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString()).append("[");
        sb.append("COLUMN=").append(this.column).append(", ");
        sb.append("TYPE=");
        if (this.type == Type.EQUAL) {
            sb.append("EQUAL");
        } else if (this.type == Type.GREATER) {
            sb.append("GREATER");
        } else if (this.type == Type.LESS) {
            sb.append("LESS");
        } else if (this.type == Type.GREATER_EQUAL) {
            sb.append("GREATER_EQUAL");
        } else if (this.type == Type.LESS_EQUAL) {
            sb.append("LESS_EQUAL");
        } else if (this.type == Type.NOT_EQUAL) {
            sb.append("NOT_EQUAL");
        } else if (this.type == Type.LIKE) {
            sb.append("LIKE");
        } else if (this.type == Type.BETWEEN) {
            sb.append("BETWEEN");
        } else if (this.type == Type.IN) {
            sb.append("IN");
        } else if (this.type == Type.GREATER_EQUAL_TIME) {
            sb.append("GREATER_EQUAL_TIME");
        } else if (this.type == Type.LESS_EQUAL_TIME) {
            sb.append("LESS_EQUAL_TIME");
        } else {
            sb.append(this.type);
        }
        sb.append(", ");
        sb.append("VALUE=");
        sb.append(this.value == null ? "null" : this.value.toString());
        sb.append("]");
        return sb.toString();
    }
}
